package com.tbynet.jwp.framework.core;

import com.jfinal.log.Log;
import com.jfinal.plugin.ehcache.CacheKit;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 通用缓存管理器
 * 负责统一的缓存操作，提供多级缓存、批量操作等高级功能
 *
 * @author 佰亿互联
 * @version 3.1
 * @since 2025-11-04
 */
public class JwpCache {

    /** 日志记录器 */
    private final Log log = Log.getLog(this.getClass());

    /** 默认缓存名称 */
    public static final String DEFAULT_CACHE = "defaultCache";

    // ========== 基础缓存操作方法 ==========

    /**
     * 获取缓存对象（支持默认值）
     *
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param defaultValue 默认值（当缓存不存在时返回）
     * @param <T> 缓存对象类型
     * @return T 缓存对象或默认值
     */
    public <T> T getCache(String cacheName, Object key, T defaultValue) {
        return executeWithCacheHandling(() -> {
            T value = CacheKit.get(cacheName, key);
            return value != null ? value : defaultValue;
        }, cacheName, key, "get");
    }

    /**
     * 设置缓存对象
     *
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param value 缓存值
     */
    public void setCache(String cacheName, Object key, Object value) {
        executeWithCacheHandling(() -> {
            CacheKit.put(cacheName, key, value);
            log.debug("缓存设置成功: %s, key: %s", cacheName, key);
            return null;
        }, cacheName, key, "set");
    }

    /**
     * 删除缓存对象
     *
     * @param cacheName 缓存名称
     * @param key 缓存键
     */
    public void removeCache(String cacheName, Object key) {
        executeWithCacheHandling(() -> {
            CacheKit.remove(cacheName, key);
            log.debug("缓存删除成功: %s, key: %s", cacheName, key);
            return null;
        }, cacheName, key, "remove");
    }

    /**
     * 检查缓存是否存在
     *
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @return boolean 是否存在
     */
    public boolean containsCache(String cacheName, Object key) {
        return executeWithCacheHandling(() -> {
            Object value = CacheKit.get(cacheName, key);
            return value != null;
        }, cacheName, key, "contains");
    }

    // ========== 高级缓存操作方法 ==========

    /**
     * 多级缓存获取（缓存穿透保护）
     *
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param loader 数据加载器（缓存不存在时调用）
     * @param <T> 数据类型
     * @return T 数据对象
     */
    public <T> T getMultiLevelCache(String cacheName, Object key, Supplier<T> loader) {
        // 第一级：本地缓存
        T value = getCache(cacheName, key, null);
        if (value != null) {
            return value;
        }

        // 第二级：数据加载（防止缓存穿透）
        try {
            value = loader.get();
            if (value != null) {
                setCache(cacheName, key, value);
            }
            return value;
        } catch (Exception e) {
            log.error("多级缓存数据加载失败: %s, key: %s", cacheName, key, e);
            return null;
        }
    }

    /**
     * 带过期时间的缓存设置
     *
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param value 缓存值
     * @param ttl 过期时间（秒）
     */
    public void setCacheWithTTL(String cacheName, Object key, Object value, int ttl) {
        executeWithCacheHandling(() -> {
            // 注意：这里需要根据实际缓存实现来设置TTL
            // Ehcache 可以通过配置缓存名称的TTL来实现
            CacheKit.put(cacheName, key, value);
            log.debug("缓存设置成功(TTL: %ss): %s, key: %s", ttl, cacheName, key);
            return null;
        }, cacheName, key, "setWithTTL");
    }

    /**
     * 批量获取缓存
     *
     * @param cacheName 缓存名称
     * @param keys 缓存键列表
     * @param <T> 数据类型
     * @return Map<Object, T> 缓存数据Map（只包含存在的缓存项）
     */
    public <T> Map<Object, T> batchGetCache(String cacheName, List<Object> keys) {
        return keys.stream()
                .map(key -> {
                    T value = getCache(cacheName, key, null);
                    return value != null ? new AbstractMap.SimpleEntry<>(key, value) : null;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue
                ));
    }

    /**
     * 批量设置缓存
     *
     * @param cacheName 缓存名称
     * @param keyValueMap 键值对Map
     */
    public void batchSetCache(String cacheName, Map<Object, Object> keyValueMap) {
        keyValueMap.forEach((key, value) -> setCache(cacheName, key, value));
    }

    /**
     * 批量删除缓存
     *
     * @param cacheName 缓存名称
     * @param keys 缓存键列表
     */
    public void batchRemoveCache(String cacheName, List<Object> keys) {
        keys.forEach(key -> removeCache(cacheName, key));
    }

    /**
     * 获取缓存键集合
     *
     * @param cacheName 缓存名称
     * @return Set<Object> 缓存键集合
     */
    public Set<Object> getCacheKeys(String cacheName) {
        return executeWithCacheHandling(() -> {
            // 注意：这个实现依赖于具体的缓存框架
            // Ehcache 可以通过 CacheManager 获取所有键
            log.warn("获取缓存键集合功能需要根据具体缓存框架实现");
            return Collections.emptySet();
        }, cacheName, null, "getKeys");
    }

    /**
     * 清空指定缓存的所有数据
     *
     * @param cacheName 缓存名称
     */
    public void clearCache(String cacheName) {
        executeWithCacheHandling(() -> {
            // 注意：这个实现依赖于具体的缓存框架
            // Ehcache 可以通过 CacheManager 清空缓存
            log.warn("清空缓存功能需要根据具体缓存框架实现");
            return null;
        }, cacheName, null, "clear");
    }

    /**
     * 获取缓存统计信息
     *
     * @param cacheName 缓存名称
     * @return Map<String, Object> 统计信息
     */
    public Map<String, Object> getCacheStats(String cacheName) {
        Map<String, Object> stats = new HashMap<>();
        try {
            // 这里可以根据具体缓存框架获取统计信息
            stats.put("cacheName", cacheName);
            stats.put("lastAccessTime", new Date());
            stats.put("status", "active");
        } catch (Exception e) {
            log.warn("获取缓存统计信息失败: %s", cacheName, e);
            stats.put("error", e.getMessage());
        }
        return stats;
    }

    // ========== 缓存模式实现 ==========

    /**
     * 缓存旁路模式（Cache-Aside）
     *
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param loader 数据加载器
     * @param <T> 数据类型
     * @return T 数据
     */
    public <T> T cacheAside(String cacheName, Object key, Supplier<T> loader) {
        return getMultiLevelCache(cacheName, key, loader);
    }

    /**
     * 读写穿透模式（Read-Through）
     * 注意：需要缓存框架支持
     *
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param loader 数据加载器
     * @param <T> 数据类型
     * @return T 数据
     */
    public <T> T readThrough(String cacheName, Object key, Supplier<T> loader) {
        // 在支持Read-Through的缓存框架中，可以直接调用
        // 这里降级为Cache-Aside模式
        return cacheAside(cacheName, key, loader);
    }

    /**
     * 写穿透模式（Write-Through）
     * 注意：需要缓存框架支持
     *
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param value 缓存值
     * @param writer 数据写入器
     */
    public void writeThrough(String cacheName, Object key, Object value, Runnable writer) {
        // 在支持Write-Through的缓存框架中，写入缓存时会自动写入底层存储
        // 这里降级为先写存储再写缓存
        executeWithCacheHandling(() -> {
            writer.run();
            setCache(cacheName, key, value);
            return null;
        }, cacheName, key, "writeThrough");
    }

    // ========== 内部工具方法 ==========

    /**
     * 带缓存异常处理的执行模板
     */
    private <T> T executeWithCacheHandling(Supplier<T> operation,
                                           String cacheName, Object key, String operationType) {
        try {
            return operation.get();
        } catch (Exception e) {
            log.warn("缓存%s失败: %s, key: %s", operationType, cacheName, key, e);
            return null;
        }
    }

    /**
     * 构建缓存键（标准化）
     *
     * @param prefix 键前缀
     * @param parts 键组成部分
     * @return String 标准化的缓存键
     */
    public String buildCacheKey(String prefix, Object... parts) {
        if (parts == null || parts.length == 0) {
            return prefix;
        }

        StringBuilder keyBuilder = new StringBuilder(prefix);
        for (Object part : parts) {
            if (part != null) {
                keyBuilder.append(":").append(part.toString());
            }
        }
        return keyBuilder.toString();
    }

    /**
     * 验证缓存名称和键的有效性
     *
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @throws IllegalArgumentException 当参数无效时抛出
     */
    private void validateCacheParams(String cacheName, Object key) {
        if (cacheName == null || cacheName.trim().isEmpty()) {
            throw new IllegalArgumentException("缓存名称不能为空");
        }
        if (key == null) {
            throw new IllegalArgumentException("缓存键不能为空");
        }
    }

    /**
     * 带超时控制的缓存获取
     *
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param loader 数据加载器
     * @param timeoutMs 超时时间(毫秒)
     * @param <T> 数据类型
     * @return 数据对象
     */
    public <T> T getWithTimeout(String cacheName, Object key, Supplier<T> loader, long timeoutMs) {
        if (timeoutMs <= 0) {
            return getMultiLevelCache(cacheName, key, loader);
        }

        // 使用CompletableFuture实现超时控制
        try {
            CompletableFuture<T> future = CompletableFuture.supplyAsync(() ->
                    getMultiLevelCache(cacheName, key, loader));

            return future.get(timeoutMs, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            log.warn("缓存获取超时: %s, key: %s, timeout: %sms", cacheName, key, timeoutMs);
            return loader.get(); // 超时后降级到直接加载
        } catch (Exception e) {
            log.warn("缓存获取异常: %s, key: %s", cacheName, key, e);
            return loader.get(); // 异常时降级到直接加载
        }
    }

    /**
     * 批量操作执行器
     * 提供统一的批量操作异常处理
     */
    public void executeBatchOperation(String operationName, Runnable operation) {
        long startTime = System.currentTimeMillis();

        try {
            operation.run();
            long duration = System.currentTimeMillis() - startTime;
            log.debug("批量操作完成: %s, 耗时: %sms", operationName, duration);
        } catch (Exception e) {
            log.error("批量操作失败: %s", operationName, e);
            throw new RuntimeException("批量操作失败: " + operationName, e);
        }
    }

    /**
     * 缓存健康检查
     */
    public Map<String, Object> healthCheck() {
        Map<String, Object> health = new HashMap<>();
        health.put("timestamp", new Date());

        try {
            // 测试基础缓存功能
            String testKey = "health_check_" + System.currentTimeMillis();
            String testValue = "test_value";

            // 测试设置
            setCache(DEFAULT_CACHE, testKey, testValue);

            // 测试获取
            String retrievedValue = getCache(DEFAULT_CACHE, testKey, null);
            boolean setGetSuccess = testValue.equals(retrievedValue);

            // 测试删除
            removeCache(DEFAULT_CACHE, testKey);
            boolean deleteSuccess = !containsCache(DEFAULT_CACHE, testKey);

            health.put("status", setGetSuccess && deleteSuccess ? "HEALTHY" : "DEGRADED");
            health.put("setGetTest", setGetSuccess ? "PASS" : "FAIL");
            health.put("deleteTest", deleteSuccess ? "PASS" : "FAIL");
            health.put("message", setGetSuccess && deleteSuccess ?
                    "缓存系统运行正常" : "缓存系统功能异常");

        } catch (Exception e) {
            health.put("status", "ERROR");
            health.put("message", "健康检查失败: " + e.getMessage());
            health.put("error", e.toString());
        }

        return health;
    }

    /**
     * 缓存内存使用分析（估算）
     */
    public Map<String, Object> analyzeMemoryUsage(String cacheName) {
        Map<String, Object> analysis = new HashMap<>();
        analysis.put("cacheName", cacheName);
        analysis.put("timestamp", new Date());

        try {
            // 注意：这个实现依赖于具体的缓存框架
            // Ehcache 可以通过 Cache 对象获取统计信息
            analysis.put("estimatedSize", "需要具体缓存框架支持");
            analysis.put("memoryUsage", "需要具体缓存框架支持");
            analysis.put("recommendation", "考虑使用Ehcache的统计功能");

        } catch (Exception e) {
            analysis.put("error", "内存分析失败: " + e.getMessage());
        }

        return analysis;
    }
}